﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using GCPUD.Autoprov.RemedyLib;
using GCPUD.Autoprov.Network;


namespace GCPUD.AutoProv
{
    class ProvisioningWatcher
    {
        #region Class variables
        List<ProvRequest> provisioningProblems = new List<ProvRequest>();
        public delegate void DataAddEventHandler(object sender, DataEventArgs e);
        public event DataAddEventHandler DataAdd = delegate { };
        public delegate void DataRemoveEventHandler(object sender, DataEventArgs e);
        public event DataRemoveEventHandler DataRemove = delegate { };
        public delegate void ProblemEventHandler(object sender, ProblemsEventArgs e);
        public event ProblemEventHandler UpdateProblems = delegate { };
        public event ProblemEventHandler ClearProblems = delegate { };
        public delegate void ServerStatusEventHandler(object sender, DataEventArgs e);
        public event ServerStatusEventHandler StatusChange = delegate { };
        
        //private RemedyServer server = new RemedyServer();
        private RemedyServer server;
        private bool w_done = false;
        private Thread w_WatcherThread;
        private AutoProvSettings settings;
        
        private List<Vlan> vlans = new List<Vlan>();
        
        #endregion
        
        public void Start(AutoProvSettings settings)
        {
            server = new RemedyServer(settings.provServer, settings.provPort, settings.provUser, settings.provPassword);
            w_WatcherThread = new Thread(new ThreadStart(Watcher));
            w_WatcherThread.Name = "Main Watch Thread";
            w_WatcherThread.IsBackground = true;
            w_WatcherThread.Start();
        }
        public void Stop()
        {
            w_done = true;
        }
        public void Watcher()
        {
            List<ProvStencil> provisionings = new List<ProvStencil>();
            List<ProvRequest> provRequests = new List<ProvRequest>();
            while (!w_done)
            {
                //Console.WriteLine("Checking for provisiong requests...");

                GetNewProvisioningFromServer(provRequests);
                foreach (ProvRequest request in provRequests.ToList())
                {
                    switch (request.ProvStatus)
                    {
                        case ProvStatus.Queued:
                            ProvisionIt(request);
                            break;
                        case ProvStatus.NeedsVlan1Config:
                            ProvisionIt(request);
                            break;
                        case ProvStatus.Error:
                            if (request.Problems.Count > 0 && !(request.Tries > 1))
                            {
                                StringBuilder problemString = new StringBuilder();
                                foreach (String problem in request.Problems)
                                {
                                    problemString.Append(problem + ":");
                                }
                                request.UpdateRemedyQueue(problemString.ToString());
                            }
                            request.Problems.Clear();
                            request.ProvStatus = ProvStatus.Queued;
                            break;
                        case ProvStatus.PortError:
                            string spliceinterface = "Gi" + request.SplicingEntry.SwitchBlade + "/" + request.SplicingEntry.SwitchPort;
                            if (request.FoundOnPort.Equals(spliceinterface))
                            {
                                request.ProvStatus = ProvStatus.Queued;
                            }
                            break;
                        case ProvStatus.Complete:
                            request.UpdateRemedyQueue("Provisioning Completed");
                            provRequests.Remove(request);
                            break;

                        case ProvStatus.NotFoundError:
                            break;
                        default:
                            break;
                    }
                    
                    if (request.Tries > 20)
                    {
                        //request.UpdateRemedyQueue("Timed out, reprovision required");
                        Console.WriteLine(DateTime.Now + ":" + request.Entry.RequestID + " Remvoed from queue due to number of tries");
                        provRequests.Remove(request);
                        continue;
                    }
                }
         
                if (provRequests != null && provRequests.Count > 0)
                {
                    foreach (ProvRequest request in provRequests.ToList())
                    {
                        if (request != null)
                        {
                            if (Remedy.Provisioning(server, request.Entry.RequestID).Gen.Equals(""))
                            {
                                DataRemove(this, new DataEventArgs(request.Entry.RequestID));
                            }
                        }
                    }
                }
                 Thread.Sleep(5000); 
            }
        }

        private void GetNewProvisioningFromServer(List<ProvRequest> provRequests)
        {
            foreach (ProvisioningEntry entry in Remedy.GetNewProvisioningRequests(server))
            {
                ProvRequest newRequest = new ProvRequest(server, entry);
                var result = provRequests.FindAll(r => r.Entry.RequestID.Equals(entry.RequestID) || r.Entry.Stencil.Equals(entry.Stencil));
                if (result == null)
                {
                    newRequest.QueueID = Remedy.CreateGatewayQueue(server, entry.Stencil, "", "Queued for Hub Provisioning");
                    DataAdd(this, new DataEventArgs(newRequest.Entry.RequestID));
                    if (NewProv(newRequest))
                    {
                        newRequest.ProvStatus = ProvStatus.Queued;
                        provRequests.Add(newRequest);
                    }
                }
            }
        }
        public void ProvisionIt(ProvRequest request)
        {
            request.Tries++;
            Thread queueProvRequest = new Thread(ProvQueue.DoRequest);
            queueProvRequest.Name = request.Entry.RequestID;
            //Remedy.UpdateGatewayQueue(server, request.QueueID, DateTime.Now.ToString(), "Completed");
            Console.WriteLine(DateTime.Now + ":" + request.Entry.RequestID + " Processing Request");
            queueProvRequest.Start(request);
        }
        public void ProvisionItOld(ProvRequest request)
        {
            request.Tries++;
            
            if (request.ProvStatus == ProvStatus.Working || request.ProvStatus == ProvStatus.NeedsVlan1Config)
            {

                //check for third octet
                Thread queueProvRequest = new Thread(ProvQueue.DoRequest);
                queueProvRequest.Name = request.Entry.RequestID;
                //Remedy.UpdateGatewayQueue(server, request.QueueID, DateTime.Now.ToString(), "Completed");
                Console.WriteLine(DateTime.Now + ":" + request.Entry.RequestID + " Queued");
                if (request.ProvStatus == ProvStatus.NeedsVlan1Config)
                {
                    queueProvRequest.Start(request);
                }
                else
                {
                    request.ProvStatus = ProvStatus.Thread;
                    queueProvRequest.Start(request);
                }
            }

            
           
        }

        private bool NewProv(ProvRequest request)
        {
            return (GetAccount(request) &&
                GetGateway(request) &&
                GetHub(request) &&
                GetSplicing(request) &&
                GetProvisioning(request));
                          
        }

        private bool GetAccount(ProvRequest request)
        {
            request.AccountEntry = Remedy.Account(server, request.Entry.AccountID);
            if (request.AccountEntry.RequestID == null)
            {
                request.ProvStatus = ProvStatus.Error;
                request.Problems.Add("Account Invalid");
                return false;
            }
            else return true;

        }
        private bool GetGateway(ProvRequest request)
        {
            List<GatewayEntry> gateways = Remedy.RemedyGateways(server, "'Stencil #' = \"" + request.Entry.Stencil + "\"");
            GatewayEntry gateway = gateways.Find(
                g => 
                    g.StencilNum == request.GatewayEntry.StencilNum &&
                    g.GatewayIP != null
                    );
            if (gateway != null)
            {
                request.GatewayEntry = gateway;
                return true;
            }
            else
            {   
                request.ProvStatus = ProvStatus.Error;
                request.Problems.Add("No Gateway Found matching stencil #:"+request.GatewayEntry.StencilNum);
                return false;
            }
        }

        private bool GetHub(ProvRequest request)
        {
            HubEntry hubEntry = Remedy.Hub(server, request.AccountEntry.PrimaryHubID);
            if (hubEntry == null || hubEntry.HubIPAddress == null)
            {
                request.ProvStatus = ProvStatus.Error;
                request.Problems.Add(hubEntry.HubName + " has an invalid IP Address!");
                return false;
            }
            else
            {
                request.HubEntry = hubEntry;
                return true;
            }
        }

        private bool GetProvisioning(ProvRequest request)
        {
            request.ProvEntries = Remedy.RemedyProvisionings(server, "'De-provisioning Date' = $NULL$ && 'Provisioning Date' != $NULL$ && 'Stencil #' = \"" + request.GatewayEntry.StencilNum + "\"");
            if (request.ProvEntries != null)
                return true;
            else
            {
                request.ProvStatus = ProvStatus.Error;
                request.Problems.Add("No provisioning found");
                return false;
            }
        }

        private bool GetSplicing(ProvRequest request)
        {
            List<SplicingEntry> requestSplicing = Remedy.RemedySplicing(server, "'Stencil #' = \"" + request.GatewayEntry.StencilNum + "\" AND 'premise_id'=\"" + request.AccountEntry.AccountPremise + "\"");
            if (requestSplicing == null || requestSplicing.Count > 1 || requestSplicing.Count < 1)
            {
                request.ProvStatus = ProvStatus.Error;
                request.Problems.Add("Either more than one splice or no splice matches stencil: " + request.Entry.Stencil + " and premise: " + request.AccountEntry.AccountPremise + "!");
                return false;            
            }
            else
            {
                SplicingEntry splicingEntry = requestSplicing.ElementAt(0);
                if (requestSplicing.ElementAt(0) == null || splicingEntry == null || splicingEntry.SwitchBlade == null || splicingEntry.SwitchPort == null || splicingEntry.SwitchBlade == "" || splicingEntry.SwitchPort == "")
                {
                    request.ProvStatus = ProvStatus.Error;
                    request.Problems.Add("Switch Blade or Port Missing");
                    return false;
                }
                else
                {

                    request.SplicingEntry = splicingEntry;
                    return true;
                }
            }
        }

    }

    public class DataEventArgs : EventArgs 
    {     
        public string Data { get; set; }     
        public DataEventArgs(string data) : base()     
        {         
            Data = data;     
        } 
    }

    public class ProblemsEventArgs : EventArgs
    {
        public List<ProvRequest> ProvRequests { get; set; }
        public ProblemsEventArgs(List<ProvRequest> provRequests) : base()
        {
            ProvRequests = provRequests;
        }
    }

    public class ServerEventArgs : EventArgs
    {
        public bool Status { get; set; }
        public ServerEventArgs(bool status) : base()
        {
            Status = status;
        }
    }
    public enum ProvStatus { New, Queued, Error, Complete, PortError, NotFoundError, Working, Thread, NeedsVlan1Config, Waiting, Ready, NotReady };
    public class ProvStencil
    {
        public string Stencil {get; set;}
        public string ProvisioningID {get; set;}

        public string QueueID { get; set; }
        public ProvStencil(string provId, string stencil)
        {
            Stencil = stencil;
            ProvisioningID = provId;
        }

    }
    public class ProvRequest : IEquatable<ProvRequest>
    {
        private ProvStatus provStatus;
        private RemedyServer server;
        private string foundOnPort;
        private int tries;
        private string queueID;
        private DateTime expires;
        private ProvisioningEntry provisioningEntry;
        private AccountEntry accountEntry;
        private GatewayEntry gatewayEntry;
        private SplicingEntry splicingEntry;
        private List<ProvisioningEntry> provEntries;
        private HubEntry hubEntry;
        private List<string> problems;

        public bool Processing = false;
        public ProvRequest(RemedyServer _server, ProvisioningEntry _provisioningEntry)
        {
            provStatus = ProvStatus.New;
            server = _server;
            tries = 0;

            expires = DateTime.Now.AddMinutes(1);
            provisioningEntry = _provisioningEntry;
            Problems = new List<string>();
        }
        public ProvStatus ProvStatus { get { return provStatus; } set { provStatus = value; } }
        public int Tries { get { return tries; } set { tries = value; } }
        public string QueueID { get { return queueID; } set { queueID = value; } }
        public List<string> Problems { get { return problems; } set { problems = value; } }
        public DateTime Expires { get { return expires; } }
        public ProvisioningEntry Entry { get { return provisioningEntry; } }

        public AccountEntry AccountEntry
        {
            get { return accountEntry; }
            set { accountEntry = value; }
        }
        public GatewayEntry GatewayEntry
        {
            get { return gatewayEntry; }
            set { gatewayEntry = value; }
        }
        public SplicingEntry SplicingEntry
        {
            get { return splicingEntry; }
            set { splicingEntry = value; }
        }
        public List<ProvisioningEntry> ProvEntries
        {
            get { return provEntries; }
            set { provEntries = value; }
        }
        public HubEntry HubEntry
        {
            get { return hubEntry; }
            set { hubEntry = value; }
        }
        public string FoundOnPort
        {
            get { return foundOnPort; }
            set { foundOnPort = value; }
        }

        public override string ToString()
        {
            return Entry.RequestID;
        }
        public override bool Equals(object obj)
        {
            if (obj == null)
                return base.Equals(obj);
            ProvRequest other = obj as ProvRequest;
            if (other == null)
                return false;
            if (this.provisioningEntry.RequestID == other.provisioningEntry.RequestID)
                return true;
            if (this.provisioningEntry.Stencil == other.provisioningEntry.Stencil)
                return true;
            else
                return false;
        }
        public bool Equals(ProvRequest other)
        {
            if (other == null)
                return base.Equals(other);
            if (this.provisioningEntry.RequestID == other.provisioningEntry.RequestID)
                return true;
            if (this.provisioningEntry.Stencil == other.provisioningEntry.Stencil)
                return true;
            else
                return false;
        }

        public override int GetHashCode()
        {
            string hashstring = this.provisioningEntry.RequestID + this.provisioningEntry.Stencil;
            return hashstring.GetHashCode();
        }
        //public static bool operator ==(ProvRequest pr1, ProvRequest pr2)
        //{
        //   if (pr2 == null && pr1 == null)
        //        return true;
        //    if (pr2 == null && pr1 != null)
        //        return false;
        //    if (pr2 != null && pr1 == null)
        //        return false;
        //    return pr1.Equals(pr2);
        //}
        //public static bool operator !=(ProvRequest pr1, ProvRequest pr2)
        //{
        //    if (pr2 == null && pr1 == null)
        //        return false;
        //    if (pr2 == null && pr1 != null)
        //        return true;
        //    if (pr2 != null && pr1 == null)
        //        return true;
         //   return !pr1.Equals(pr2);
        //}
        public void UpdateRemedyQueue(string message)
        {
            Remedy.UpdateGatewayQueue(server, queueID, DateTime.Now.ToString(), message);
        }

    }
}
